Skip to content

The design agent is now drafting the implementation plan covering the…#39

Merged
BotCoder254 merged 1 commit into
mainfrom
feat/ai-commit-message-subagent
Jul 4, 2026
Merged

The design agent is now drafting the implementation plan covering the…#39
BotCoder254 merged 1 commit into
mainfrom
feat/ai-commit-message-subagent

Conversation

@BotCoder254

@BotCoder254 BotCoder254 commented Jul 4, 2026

Copy link
Copy Markdown
Owner

… File Writer IPC surface, incremental auto-reindex, file icons, and the bottom-gap fix. I'll review its output against the code and write the final plan when it returns.

Summary

Type of change

  • Bug fix
  • New feature
  • Refactor / internal change
  • Documentation
  • Build / CI / tooling

Architectural reasoning

Verification

  • npm run lint passes
  • npx vite build --config vite.renderer.config.mts passes
  • App still starts with npm start (for main/preload changes)
  • Manual testing steps described below

Security checklist (if touching main process)

  • All renderer-supplied IPC inputs are validated in the main process
  • SQL uses bound parameters only
  • Any spawned process uses argv arrays (no shell: true)
  • Renderer-supplied paths are guarded against traversal
  • No weakening of contextIsolation / sandbox / CSP / navigation lockdown

Documentation and changelog

  • Updated the relevant page(s) under docs/
  • Added a CHANGELOG.md entry (if user-facing)
  • Screenshots attached (for UI changes)

Theme discipline (for UI changes)

  • Uses design tokens only (no off-palette hex, no dark: variants, no gradients)

Note

Medium Risk
New main-process filesystem mutation surface is security-sensitive, though layered IPC and writer guards mitigate traversal/symlink risks; layout and search indexing changes are lower risk.

Overview
Adds a guarded File Writer path in main (fs/writer.ts) and wires it through new fs:* IPC, preload window.limboo.fs methods, and FileSystemManager mutations (atomic write, create file/dir, delete, rename/move, copy). IPC validates paths, content size, and rebuilds mutation options from booleans only; the writer enforces workspace containment, realpath symlink checks, .git protection, and size/entry caps.

Watcher batches now emit WatchBatch (changed paths + structural flag). Small batches trigger SearchManager.indexFiles incremental FTS updates; structural/large batches still do a full reindex. Mutations record expanded File History actions and refresh tree/git/search via the same path as external changes.

Renderer: floating app shell (title bar + side rails on bg-base, center + drawer in one bg-surface card with gutters and a ghost resize handle), per-language file icons, and a Files tree context menu (FileTreeMenu) backed by useFileSystemStore create/rename/delete with error toasts. Search indexing gains broader language detection and symbol rules (e.g. PowerShell, Lua).

Docs updated for the shell layout and window.limboo fs write API.

Reviewed by Cursor Bugbot for commit 6baaea8. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Added file and folder actions from the file tree, including create, rename, copy path, reveal, and delete.
    • Improved file browsing with better file-type icons and broader language/document detection.
    • Refined the app layout into a floating workspace style with updated spacing and panels.
  • Bug Fixes

    • Strengthened workspace file actions to better prevent invalid paths and unsafe edits.
    • Improved search updates after file changes for more responsive results.
  • Documentation

    • Expanded API and app-shell documentation to reflect the latest workspace and file management behavior.

… File Writer IPC surface, incremental auto-reindex, file icons, and the bottom-gap fix. I'll review its output against the code and write the final plan when it returns.

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR successfully implements the File Writer component with comprehensive security controls and proper validation. The implementation demonstrates excellent defense-in-depth security practices across multiple layers.

Key Security Strengths

Multi-Layer Validation

  • IPC handlers validate all inputs before reaching the File System Manager
  • File Writer performs additional defense-in-depth validation
  • All paths are constrained: bounded, NUL-free, relative, non-traversing

Path Traversal Protection

  • Multiple checks prevent directory escape attempts
  • Symlink resolution with containment re-validation prevents redirection attacks
  • .git directory protection maintains repository integrity

Safe Operations

  • Atomic writes using temp files prevent corruption
  • Bounded recursive operations prevent DoS
  • Prototype pollution protection in options sanitization
  • File operations never follow symlinks

Architecture

The implementation follows the documented security model (CLAUDE.md §6) correctly:

  1. IPC boundary validates and sanitizes all renderer inputs
  2. File Writer enforces workspace containment
  3. Operations are atomic, bounded, and safe

Verification Status

Required Actions:

  • ✅ All security validations implemented correctly
  • ✅ Path containment enforced at multiple layers
  • ✅ Atomic operations prevent file corruption
  • ✅ DOS protection with bounded operations

Recommended Before Merge:

  • Complete the checklist items in the PR description
  • Run npm run lint and build verification
  • Manual testing of file write operations
  • Verify the integrated watcher triggers incremental reindexing correctly

The code quality is excellent with no blocking defects identified. Once the PR checklist is completed and tests pass, this is ready to merge.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a guarded File Writer feature: a new writer module with symlink/traversal protections, FileSystemManager mutation methods, IPC handlers, preload bindings, a renderer store with error toasts, and a file-tree context menu. It also adds batched watcher events with incremental search indexing, expanded language/icon detection, and restyles the app shell layout.

Changes

File Writer Feature

Layer / File(s) Summary
Shared contracts and constants
src/shared/types.ts, src/shared/constants.ts, src/shared/ipc-channels.ts
Adds FileWriteResult/FsMutationOptions types, expands FileHistoryEntry.action, adds FS_LIMITS caps, and new fs:* IPC channels.
Guarded writer module
src/main/managers/fs/writer.ts
Adds path-containment/symlink-escape checks, atomic write, and write/create/delete/rename/copy operations with bounded recursion.
Batched watcher and manager coordination
src/main/managers/fs/watcher.ts, src/main/managers/FileSystemManager.ts, src/main/managers/search/SearchManager.ts
Adds WatchBatch, wires mutation methods with history recording, and centralizes post-mutation indexing/git/search refresh with incremental vs full reindex logic.
IPC handlers and preload bridge
src/main/ipc/fsHandlers.ts, src/preload/index.ts
Adds validation helpers and new handlers/invoke bindings for write/create/delete/rename/copy.
Store and context-menu UI
src/renderer/stores/useFileSystemStore.ts, src/renderer/lib/fileIcons.tsx, src/renderer/features/activity/FileTree*.tsx, panels.tsx
Adds toast-on-error mutation handling, a file-icon resolver, and a right-click context menu for create/rename/delete/copy-path/reveal.
Search language detection
src/main/managers/search/query.ts, src/main/managers/search/symbols.ts
Expands language detection tables and adds PowerShell/Lua symbol rules.
Documentation
CLAUDE.md, docs/reference/window-limboo-api.md
Updates roadmap and API reference docs to describe the File Writer gateway.

Estimated code review effort: 4 (Complex) | ~60 minutes

Floating App Shell Restyle

Layer / File(s) Summary
AppShell layout and ghost resize handle
src/renderer/app/AppShell.tsx, src/renderer/components/ui/ResizeHandle.tsx
Wraps center workspace and drawer into a floating rounded card and adds a ghost mode to ResizeHandle.
Panel background/border restyle
TitleBar.tsx, ActivityRail.tsx, ActivityDrawer.tsx, SessionsSidebar.tsx, GitPanel.tsx, MemoryPanel.tsx, TerminalPanel.tsx, CenterWorkspace.tsx, Composer.tsx
Removes border classes and adjusts bg-base/bg-surface usage across panels.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FileTreeMenu
  participant useFileSystemStore
  participant PreloadFsApi
  participant fsHandlers
  participant FileSystemManager
  participant Writer as fs/writer

  FileTreeMenu->>useFileSystemStore: createFile/rename/remove(workspaceId, path)
  useFileSystemStore->>PreloadFsApi: ipcRenderer.invoke(fs channel)
  PreloadFsApi->>fsHandlers: fs:writeFile / fs:rename / fs:delete
  fsHandlers->>FileSystemManager: writeFile/rename/deleteEntry(...)
  FileSystemManager->>Writer: resolveMutationTarget + atomicWrite/rename/delete
  Writer-->>FileSystemManager: FileWriteResult
  FileSystemManager->>FileSystemManager: record FileHistory, afterMutation()
  FileSystemManager-->>fsHandlers: result
  fsHandlers-->>PreloadFsApi: result
  PreloadFsApi-->>useFileSystemStore: result
  useFileSystemStore-->>FileTreeMenu: success/false (toast on failure)
Loading
sequenceDiagram
  participant WorkspaceWatcher
  participant FileSystemManager
  participant SearchManager
  participant GitManager

  WorkspaceWatcher->>FileSystemManager: onChange(WatchBatch: paths, structural)
  FileSystemManager->>FileSystemManager: onWatchedChange -> afterMutation
  FileSystemManager->>GitManager: refresh git status
  FileSystemManager->>SearchManager: scheduleSearchIndex(paths, structural)
  alt structural or too many paths
    SearchManager->>SearchManager: indexWorkspace (full reindex)
  else small path set
    SearchManager->>SearchManager: indexFiles (incremental reindex)
  end
  SearchManager-->>FileSystemManager: notifyChanged()
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is truncated and too vague to describe the actual code changes in this PR. Replace it with a concise title naming the main change, such as the filesystem mutation and floating app shell updates.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ai-commit-message-subagent

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
src/renderer/features/activity/FileTreeMenu.tsx (3)

85-121: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Menu can render off-screen near viewport edges.

positioned places the menu at the raw clientX/clientY with no clamping against window.innerWidth/innerHeight. Right-clicking near the right or bottom edge of the window can push menu items (especially the wider 224px w-56 edit panel) partially or fully off-screen and unreachable.

Consider clamping the coordinates (either with an estimated width/height, or by measuring the rendered menu via ref + useLayoutEffect and repositioning once mounted).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/features/activity/FileTreeMenu.tsx` around lines 85 - 121, The
context menu positioning in FileTreeMenu is using raw point.x/point.y, so the
menu can render off-screen near the right or bottom viewport edges. Update the
positioning logic around the positioned value in FileTreeMenu to clamp the
coordinates against window.innerWidth and window.innerHeight, or measure the
rendered menu through ref with useLayoutEffect and adjust after mount. Make sure
both the edit panel and the normal menu stay fully visible when opened near the
viewport boundaries.

116-216: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Menu lacks ARIA menu semantics.

The dropdown and its items are plain div/button elements with no role="menu"/role="menuitem", and no item receives focus when the menu opens (only the rename/create input auto-focuses). Adding basic ARIA roles (and optionally auto-focusing the first item) would improve screen-reader and keyboard support for this new interactive surface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/features/activity/FileTreeMenu.tsx` around lines 116 - 216, The
FileTreeMenu dropdown currently renders as plain container/button markup without
menu semantics, so update the root menu wrapper and MenuItem in FileTreeMenu to
use proper ARIA roles (menu/menuitem) and keyboard-friendly focus behavior. Add
the relevant role attributes and ensure an item is focusable or auto-focused
when the menu opens, especially for the non-editing actions like Reveal in
Explorer, Copy path, and Delete, so the menu is accessible without relying only
on the rename/create inputs.

31-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Validation/path logic lives directly in a presentational component.

isValidName and the path-composition logic in commit() are domain/business logic embedded in a feature component file. As per path instructions for src/renderer/features/**/!(*.test).{ts,tsx}: "Keep renderer components presentational: components should not contain business logic; use feature folders plus stores for domain behavior." Moving isValidName (and ideally the target-path computation) into a shared lib (e.g. alongside fileIcons.tsx) or into useFileSystemStore would keep this component focused on rendering/interaction and make the validation reusable/testable independently.

Also applies to: 64-83

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/features/activity/FileTreeMenu.tsx` around lines 31 - 36, Move
the name validation and target-path construction out of FileTreeMenu into domain
logic, since this component currently mixes presentation with business rules.
Extract isValidName and the commit() path-composition behavior into a shared
helper or useFileSystemStore, keeping FileTreeMenu focused on rendering and user
interaction. Use the existing symbols isValidName and commit() as the main entry
points when relocating the logic so the component stays thin and the rules
become reusable/testable.

Source: Path instructions

src/renderer/stores/useFileSystemStore.ts (1)

98-121: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider consolidating the repeated api-guard boilerplate.

createFile, createDir, remove, and rename all repeat the identical const api = fsApi(); if (!api) return false; guard before delegating to runMutation. A small helper (e.g. withApi(fn, label) that resolves the api once and wraps the call) would remove the duplication and reduce the chance of one method drifting from the pattern later.

♻️ Example consolidation
+async function guardedMutation(
+  op: (api: NonNullable<ReturnType<typeof fsApi>>) => Promise<unknown>,
+  label: string,
+): Promise<boolean> {
+  const api = fsApi();
+  if (!api) return false;
+  return runMutation(() => op(api), label);
+}
+
   createFile: async (workspaceId, relPath) => {
-    const api = fsApi();
-    if (!api) return false;
-    return runMutation(() => api.createFile(workspaceId, relPath), 'Could not create file');
+    return guardedMutation((api) => api.createFile(workspaceId, relPath), 'Could not create file');
   },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/stores/useFileSystemStore.ts` around lines 98 - 121, The methods
createFile, createDir, remove, and rename in useFileSystemStore all repeat the
same fsApi() null-check before calling runMutation. Extract that shared guard
into a small helper such as withApi(fn, label) or similar, then have each method
delegate through it so the api is resolved once and the mutation/error-label
pattern stays consistent across these operations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 200-208: The ASCII diagram in the documentation is still an
unlabeled fenced block, which will keep triggering the markdown lint warning.
Update the fenced block around the diagram in CLAUDE.md to use an explicit
language tag such as text, and keep the diagram content unchanged so the doc
linter recognizes it correctly.

In `@src/main/managers/fs/writer.ts`:
- Around line 56-102: resolveMutationTarget currently blocks literal “.git”
segments but can still allow symlinked paths that resolve into .git after
realpath checks. Update the writer flow in fs/writer.ts so the .git guard is
applied again on the resolved path or resolved ancestor chain inside
resolveMutationTarget before any mutation is returned, and ensure
write/mkdir/delete/rename/copy callers still rely on this helper for
enforcement.

In `@src/renderer/features/activity/FileTreeMenu.tsx`:
- Around line 173-186: The delete action in FileTreeMenu is using the same
MenuItem for both “Delete” and “Confirm delete,” which allows a rapid
double-click to bypass the intended confirmation. Update the delete flow in
FileTreeMenu/MenuItem handling so the first click only arms confirmation for a
short delay or via a distinct confirm affordance, and ensure the actual
remove(workspaceId, node.path, node.type === 'dir') call can only happen after
that deliberate second step; if you keep the current confirm state, add a brief
disable/debounce or separate confirm/cancel UI to prevent same-position
double-click execution.

---

Nitpick comments:
In `@src/renderer/features/activity/FileTreeMenu.tsx`:
- Around line 85-121: The context menu positioning in FileTreeMenu is using raw
point.x/point.y, so the menu can render off-screen near the right or bottom
viewport edges. Update the positioning logic around the positioned value in
FileTreeMenu to clamp the coordinates against window.innerWidth and
window.innerHeight, or measure the rendered menu through ref with
useLayoutEffect and adjust after mount. Make sure both the edit panel and the
normal menu stay fully visible when opened near the viewport boundaries.
- Around line 116-216: The FileTreeMenu dropdown currently renders as plain
container/button markup without menu semantics, so update the root menu wrapper
and MenuItem in FileTreeMenu to use proper ARIA roles (menu/menuitem) and
keyboard-friendly focus behavior. Add the relevant role attributes and ensure an
item is focusable or auto-focused when the menu opens, especially for the
non-editing actions like Reveal in Explorer, Copy path, and Delete, so the menu
is accessible without relying only on the rename/create inputs.
- Around line 31-36: Move the name validation and target-path construction out
of FileTreeMenu into domain logic, since this component currently mixes
presentation with business rules. Extract isValidName and the commit()
path-composition behavior into a shared helper or useFileSystemStore, keeping
FileTreeMenu focused on rendering and user interaction. Use the existing symbols
isValidName and commit() as the main entry points when relocating the logic so
the component stays thin and the rules become reusable/testable.

In `@src/renderer/stores/useFileSystemStore.ts`:
- Around line 98-121: The methods createFile, createDir, remove, and rename in
useFileSystemStore all repeat the same fsApi() null-check before calling
runMutation. Extract that shared guard into a small helper such as withApi(fn,
label) or similar, then have each method delegate through it so the api is
resolved once and the mutation/error-label pattern stays consistent across these
operations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 050e1c61-b8e9-4b8e-a092-8700f1c3a039

📥 Commits

Reviewing files that changed from the base of the PR and between 583809a and 6baaea8.

📒 Files selected for processing (29)
  • CLAUDE.md
  • docs/reference/window-limboo-api.md
  • src/main/ipc/fsHandlers.ts
  • src/main/managers/FileSystemManager.ts
  • src/main/managers/fs/watcher.ts
  • src/main/managers/fs/writer.ts
  • src/main/managers/search/SearchManager.ts
  • src/main/managers/search/query.ts
  • src/main/managers/search/symbols.ts
  • src/preload/index.ts
  • src/renderer/app/AppShell.tsx
  • src/renderer/components/layout/TitleBar.tsx
  • src/renderer/components/ui/ResizeHandle.tsx
  • src/renderer/features/activity/ActivityDrawer.tsx
  • src/renderer/features/activity/ActivityRail.tsx
  • src/renderer/features/activity/FileTree.tsx
  • src/renderer/features/activity/FileTreeMenu.tsx
  • src/renderer/features/activity/panels.tsx
  • src/renderer/features/git/GitPanel.tsx
  • src/renderer/features/memory/MemoryPanel.tsx
  • src/renderer/features/sessions/SessionsSidebar.tsx
  • src/renderer/features/terminal/TerminalPanel.tsx
  • src/renderer/features/workspace/CenterWorkspace.tsx
  • src/renderer/features/workspace/Composer.tsx
  • src/renderer/lib/fileIcons.tsx
  • src/renderer/stores/useFileSystemStore.ts
  • src/shared/constants.ts
  • src/shared/ipc-channels.ts
  • src/shared/types.ts

Comment thread CLAUDE.md
Comment on lines 200 to 208
```
┌──────────────────────────────────────────────────────────────┐
│ TitleBar (frameless, draggable) [search][_][□][x] │
├────────┬──────────────────────────────────────────┬──────────┤
│Sessions│ header / conversation / Composer │ drawer │▐ rail
└────────┴──────────────────────────────────────────┴──────────┘
│ TitleBar (root bg, frameless, draggable) [search][_][□][x] │
│ ╷ ┌────────────────────────────────────────┐ │
│Sessions│ │ header / conversation / Composer │drawer│ ▐ rail │
│(root bg)│ │ floating card (bg-surface) │ (root bg)│
│ ╵ └────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Label the ASCII diagram fence.

This is still an unlabeled fenced block, so the markdownlint warning will keep firing. Mark it as text (or another explicit language) to satisfy the doc linter.

Fix
-```
+```text
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
┌──────────────────────────────────────────────────────────────┐
│ TitleBar (frameless, draggable) [search][_][][x]
├────────┬──────────────────────────────────────────┬──────────┤
│Sessions│ header / conversation / Composer │ drawer │▐ rail
└────────┴──────────────────────────────────────────┴──────────┘
│ TitleBar (root bg, frameless, draggable) [search][_][][x]
│ ╷ ┌────────────────────────────────────────┐ │
│Sessions│ │ header / conversation / Composer │drawer│ ▐ rail │
│(root bg)│ │ floating card (bg-surface) │ (root bg)│
│ ╵ └────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
```
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 200-200: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLAUDE.md` around lines 200 - 208, The ASCII diagram in the documentation is
still an unlabeled fenced block, which will keep triggering the markdown lint
warning. Update the fenced block around the diagram in CLAUDE.md to use an
explicit language tag such as text, and keep the diagram content unchanged so
the doc linter recognizes it correctly.

Source: Linters/SAST tools

Comment on lines +56 to +102
function resolveMutationTarget(root: string, relPath: string): string {
const rel = (relPath ?? '').trim();
if (!rel) throw new FileWriteError('Refusing to mutate the workspace root.');

const segments = rel.split(/[\\/]+/).filter((s) => s.length > 0 && s !== '.');
if (segments.length === 0) throw new FileWriteError('Refusing to mutate the workspace root.');
if (segments.some((s) => s === '..')) {
throw new FileWriteError('Path escapes the workspace root.');
}
if (segments.some((s) => s.toLowerCase() === '.git')) {
throw new FileWriteError('Refusing to touch the .git directory.');
}

const target = path.resolve(root, segments.join(path.sep));
if (!isInsideRoot(root, target) || target === path.resolve(root)) {
throw new FileWriteError('Path escapes the workspace root.');
}

// The target may not exist yet, so realpath the deepest EXISTING ancestor and
// re-assert containment against the REAL root — a symlinked directory in the
// chain can never redirect the mutation outside the workspace.
let realRoot: string;
try {
realRoot = fs.realpathSync(root);
} catch {
throw new FileWriteError('Workspace root is not accessible.');
}
let probe = target;
for (;;) {
let real: string | null = null;
try {
real = fs.realpathSync(probe);
} catch {
real = null;
}
if (real !== null) {
if (!isInsideRoot(realRoot, real)) {
throw new FileWriteError('Resolved path escapes the workspace root.');
}
break;
}
const parent = path.dirname(probe);
if (parent === probe) break; // hit the filesystem root — nothing existed
probe = parent;
}
return target;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the file and locate the relevant helpers.
git ls-files 'src/main/managers/fs/*' 'src/main/managers/**/*' | sed -n '1,200p'
printf '\n--- outline ---\n'
ast-grep outline src/main/managers/fs/writer.ts --view expanded || true

printf '\n--- relevant slices ---\n'
sed -n '1,220p' src/main/managers/fs/writer.ts
printf '\n--- tail ---\n'
sed -n '220,380p' src/main/managers/fs/writer.ts

Repository: BotCoder254/limboo

Length of output: 13754


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- src/main/managers/workspace/validate.ts ---'
sed -n '1,220p' src/main/managers/workspace/validate.ts

printf '\n%s\n' '--- search for resolveMutationTarget callers / symlink handling ---'
rg -n "resolveMutationTarget\\(|Refusing to write through a symlink|Refusing to touch the \\.git directory|skip symlinks entirely|lstatSync\\(" src/main/managers/fs/writer.ts src/main/managers -g'*.ts'

Repository: BotCoder254/limboo

Length of output: 6318


Block symlinked paths that resolve into .git in resolveMutationTarget
The literal segment check misses parent symlinks: a path can resolve into .git and still pass the current realpath containment check. Re-run the .git guard on the resolved path (or resolved ancestor chain) before any write/mkdir/delete/rename/copy through this helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/managers/fs/writer.ts` around lines 56 - 102, resolveMutationTarget
currently blocks literal “.git” segments but can still allow symlinked paths
that resolve into .git after realpath checks. Update the writer flow in
fs/writer.ts so the .git guard is applied again on the resolved path or resolved
ancestor chain inside resolveMutationTarget before any mutation is returned, and
ensure write/mkdir/delete/rename/copy callers still rely on this helper for
enforcement.

Comment on lines +173 to +186
<MenuItem
icon={Trash2}
label={confirmDelete ? 'Confirm delete' : 'Delete'}
danger
onClick={() => {
if (!confirmDelete) {
setConfirmDelete(true);
return;
}
void remove(workspaceId, node.path, node.type === 'dir');
onClose();
}}
/>
</>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Rapid double-click bypasses the delete confirmation.

The delete button toggles its own label from "Delete" to "Confirm delete" and executes on the very next click of the same button, at the same screen position. React's re-render completes well before a human double-click registers (~200-400ms), so a normal double-click (or two quick clicks — a common habit) on "Delete" will silently execute an irreversible, potentially recursive (node.type === 'dir') deletion instead of requiring a deliberate second decision.

Guard against this with a short arm-delay, a timestamp-based debounce, or a distinct confirm affordance (e.g., a separate "Cancel"/"Confirm" pair, or briefly disabling the button after the first click).

🛡️ Example fix: debounce the confirm click
+  const armedAtRef = useRef(0);
   <MenuItem
     icon={Trash2}
     label={confirmDelete ? 'Confirm delete' : 'Delete'}
     danger
     onClick={() => {
       if (!confirmDelete) {
         setConfirmDelete(true);
+        armedAtRef.current = Date.now();
         return;
       }
+      if (Date.now() - armedAtRef.current < 400) return; // ignore rapid double-click
       void remove(workspaceId, node.path, node.type === 'dir');
       onClose();
     }}
   />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/features/activity/FileTreeMenu.tsx` around lines 173 - 186, The
delete action in FileTreeMenu is using the same MenuItem for both “Delete” and
“Confirm delete,” which allows a rapid double-click to bypass the intended
confirmation. Update the delete flow in FileTreeMenu/MenuItem handling so the
first click only arms confirmation for a short delay or via a distinct confirm
affordance, and ensure the actual remove(workspaceId, node.path, node.type ===
'dir') call can only happen after that deliberate second step; if you keep the
current confirm state, add a brief disable/debounce or separate confirm/cancel
UI to prevent same-position double-click execution.

@BotCoder254 BotCoder254 merged commit fe8d1d0 into main Jul 4, 2026
5 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant